{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Part 1: Imports and Configuration\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Standard library imports\n",
        "import os\n",
        "import glob\n",
        "import base64\n",
        "import io\n",
        "from datetime import datetime, timedelta\n",
        "from pathlib import Path\n",
        "from email.utils import parsedate_to_datetime\n",
        "\n",
        "# Third-party imports\n",
        "from dotenv import load_dotenv\n",
        "import gradio as gr\n",
        "\n",
        "# Google API imports\n",
        "from google.auth.transport.requests import Request\n",
        "from google.oauth2.credentials import Credentials\n",
        "from google_auth_oauthlib.flow import InstalledAppFlow\n",
        "from googleapiclient.discovery import build\n",
        "from googleapiclient.http import MediaIoBaseDownload\n",
        "\n",
        "# LangChain imports\n",
        "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
        "from langchain.schema import Document\n",
        "from langchain_openai import OpenAIEmbeddings, ChatOpenAI\n",
        "from langchain_chroma import Chroma\n",
        "from langchain.memory import ConversationBufferMemory\n",
        "from langchain.chains import ConversationalRetrievalChain\n",
        "\n",
        "# Visualization imports\n",
        "import numpy as np\n",
        "from sklearn.manifold import TSNE\n",
        "import plotly.graph_objects as go\n",
        "\n",
        "# Document processing imports\n",
        "import PyPDF2\n",
        "from docx import Document as DocxDocument\n",
        "\n",
        "print(\"All imports successful!\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Configuration\n",
        "MODEL = \"gpt-4o-mini\"  # Cost-effective model\n",
        "DB_NAME = \"vector_db_gmail_drive\"\n",
        "CREDENTIALS_DIR = \"credentials\"\n",
        "TOKENS_DIR = \"tokens\"\n",
        "\n",
        "# Google API Scopes\n",
        "SCOPES = [\n",
        "    'https://www.googleapis.com/auth/gmail.readonly',\n",
        "    'https://www.googleapis.com/auth/drive.readonly'\n",
        "]\n",
        "\n",
        "# Create necessary directories\n",
        "Path(CREDENTIALS_DIR).mkdir(exist_ok=True)\n",
        "Path(TOKENS_DIR).mkdir(exist_ok=True)\n",
        "Path(DB_NAME).mkdir(exist_ok=True)\n",
        "\n",
        "# Load environment variables\n",
        "load_dotenv(override=True)\n",
        "os.environ['OPENAI_API_KEY'] = os.getenv('OPENAI_API_KEY', 'your-key-if-not-using-env')\n",
        "\n",
        "print(f\"Configuration complete\")\n",
        "print(f\"Model: {MODEL}\")\n",
        "print(f\"Vector DB: {DB_NAME}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Part 2: Google Authentication\n",
        "\n",
        "This cell will authenticate you with Google. On first run, it will open a browser window asking you to log in and grant permissions.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def authenticate_google():\n",
        "    \"\"\"\n",
        "    Authenticate with Google APIs (Gmail + Drive)\n",
        "    Returns credentials object\n",
        "    \"\"\"\n",
        "    creds = None\n",
        "    token_path = os.path.join(TOKENS_DIR, 'token.json')\n",
        "    credentials_path = os.path.join(CREDENTIALS_DIR, 'google_credentials.json')\n",
        "    \n",
        "    # Check if credentials file exists\n",
        "    if not os.path.exists(credentials_path):\n",
        "        print(\"ERROR: google_credentials.json not found!\")\n",
        "        print(f\"Please place your credentials file at: {credentials_path}\")\n",
        "        print(\"See setup instructions above for how to create it.\")\n",
        "        return None\n",
        "    \n",
        "    # Load existing token if available\n",
        "    if os.path.exists(token_path):\n",
        "        creds = Credentials.from_authorized_user_file(token_path, SCOPES)\n",
        "    \n",
        "    # If no valid credentials, authenticate\n",
        "    if not creds or not creds.valid:\n",
        "        if creds and creds.expired and creds.refresh_token:\n",
        "            print(\"Refreshing expired credentials...\")\n",
        "            creds.refresh(Request())\n",
        "        else:\n",
        "            print(\"Opening browser for authentication...\")\n",
        "            print(\"Please log in and grant permissions.\")\n",
        "            flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)\n",
        "            creds = flow.run_local_server(port=0)\n",
        "        \n",
        "        # Save credentials for next time\n",
        "        with open(token_path, 'w') as token:\n",
        "            token.write(creds.to_json())\n",
        "        print(\"Credentials saved!\")\n",
        "    \n",
        "    print(\"Authentication successful!\")\n",
        "    return creds\n",
        "\n",
        "# Test authentication\n",
        "print(\"Testing Google authentication...\")\n",
        "creds = authenticate_google()\n",
        "if creds:\n",
        "    print(\"Ready to access Gmail and Drive!\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": []
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def fetch_gmail_emails(creds, max_results=100, days_back=180):\n",
        "    \"\"\"\n",
        "    Fetch emails from Gmail\n",
        "    \n",
        "    Args:\n",
        "        creds: Google credentials\n",
        "        max_results: Maximum number of emails to fetch\n",
        "        days_back: How many days back to search\n",
        "    \n",
        "    Returns:\n",
        "        List of Document objects\n",
        "    \"\"\"\n",
        "    print(f\"\\nFetching emails from last {days_back} days (max {max_results})...\")\n",
        "    \n",
        "    try:\n",
        "        service = build('gmail', 'v1', credentials=creds)\n",
        "        \n",
        "        # Calculate date for filtering\n",
        "        after_date = datetime.now() - timedelta(days=days_back)\n",
        "        query = f\"after:{after_date.strftime('%Y/%m/%d')}\"\n",
        "        \n",
        "        # Get list of messages\n",
        "        results = service.users().messages().list(\n",
        "            userId='me',\n",
        "            maxResults=max_results,\n",
        "            q=query\n",
        "        ).execute()\n",
        "        \n",
        "        messages = results.get('messages', [])\n",
        "        \n",
        "        if not messages:\n",
        "            print(\"No emails found.\")\n",
        "            return []\n",
        "        \n",
        "        print(f\"Found {len(messages)} emails. Processing...\")\n",
        "        \n",
        "        documents = []\n",
        "        for i, message in enumerate(messages, 1):\n",
        "            if i % 10 == 0:\n",
        "                print(f\"Processed {i}/{len(messages)} emails...\")\n",
        "            \n",
        "            # Get full message\n",
        "            msg = service.users().messages().get(\n",
        "                userId='me',\n",
        "                id=message['id'],\n",
        "                format='full'\n",
        "            ).execute()\n",
        "            \n",
        "            # Extract headers\n",
        "            headers = msg['payload']['headers']\n",
        "            subject = next((h['value'] for h in headers if h['name'] == 'Subject'), 'No Subject')\n",
        "            sender = next((h['value'] for h in headers if h['name'] == 'From'), 'Unknown')\n",
        "            date_str = next((h['value'] for h in headers if h['name'] == 'Date'), '')\n",
        "            \n",
        "            # Extract body\n",
        "            body = \"\"\n",
        "            if 'parts' in msg['payload']:\n",
        "                for part in msg['payload']['parts']:\n",
        "                    if part['mimeType'] == 'text/plain':\n",
        "                        if 'data' in part['body']:\n",
        "                            body = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8', errors='ignore')\n",
        "                            break\n",
        "            elif 'body' in msg['payload'] and 'data' in msg['payload']['body']:\n",
        "                body = base64.urlsafe_b64decode(msg['payload']['body']['data']).decode('utf-8', errors='ignore')\n",
        "            \n",
        "            # Create document\n",
        "            content = f\"Subject: {subject}\\nFrom: {sender}\\nDate: {date_str}\\n\\n{body}\"\n",
        "            \n",
        "            doc = Document(\n",
        "                page_content=content,\n",
        "                metadata={\n",
        "                    'source': 'Gmail',\n",
        "                    'type': 'email',\n",
        "                    'subject': subject,\n",
        "                    'sender': sender,\n",
        "                    'date': date_str,\n",
        "                    'message_id': message['id']\n",
        "                }\n",
        "            )\n",
        "            documents.append(doc)\n",
        "        \n",
        "        print(f\"Successfully processed {len(documents)} emails\")\n",
        "        return documents\n",
        "        \n",
        "    except Exception as e:\n",
        "        print(f\"Error fetching Gmail: {str(e)}\")\n",
        "        return []\n",
        "\n",
        "# Test Gmail fetching (adjust parameters as needed)\n",
        "if creds:\n",
        "    gmail_docs = fetch_gmail_emails(creds, max_results=50, days_back=90)\n",
        "    print(f\"\\nGmail Summary: {len(gmail_docs)} emails loaded\")\n",
        "    if gmail_docs:\n",
        "        print(f\"Sample subject: {gmail_docs[0].metadata['subject'][:60]}...\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def extract_text_from_file(file_content, mime_type, file_name):\n",
        "    \"\"\"Extract text from various file types\"\"\"\n",
        "    try:\n",
        "        if mime_type == 'text/plain' or file_name.endswith(('.txt', '.md')):\n",
        "            return file_content.decode('utf-8', errors='ignore')\n",
        "        \n",
        "        elif mime_type == 'application/pdf' or file_name.endswith('.pdf'):\n",
        "            pdf_file = io.BytesIO(file_content)\n",
        "            pdf_reader = PyPDF2.PdfReader(pdf_file)\n",
        "            text = \"\"\n",
        "            for page in pdf_reader.pages:\n",
        "                text += page.extract_text() + \"\\n\"\n",
        "            return text\n",
        "        \n",
        "        elif mime_type in ['application/vnd.openxmlformats-officedocument.wordprocessingml.document', \n",
        "                           'application/msword'] or file_name.endswith('.docx'):\n",
        "            doc_file = io.BytesIO(file_content)\n",
        "            doc = DocxDocument(doc_file)\n",
        "            text = \"\\n\".join([para.text for para in doc.paragraphs])\n",
        "            return text\n",
        "        \n",
        "        elif mime_type == 'application/vnd.google-apps.document':\n",
        "            return \"[Google Doc - will be exported as plain text]\"\n",
        "        \n",
        "        else:\n",
        "            return f\"[Unsupported file type: {mime_type}]\"\n",
        "            \n",
        "    except Exception as e:\n",
        "        return f\"[Error extracting text: {str(e)}]\"\n",
        "\n",
        "def fetch_drive_files(creds, max_results=50):\n",
        "    \"\"\"\n",
        "    Fetch files from Google Drive\n",
        "    \n",
        "    Args:\n",
        "        creds: Google credentials\n",
        "        max_results: Maximum number of files to fetch\n",
        "    \n",
        "    Returns:\n",
        "        List of Document objects\n",
        "    \"\"\"\n",
        "    print(f\"\\nFetching files from Google Drive (max {max_results})...\")\n",
        "    \n",
        "    try:\n",
        "        service = build('drive', 'v3', credentials=creds)\n",
        "        \n",
        "        # Query for text-based files\n",
        "        query = \"(mimeType='text/plain' or mimeType='application/pdf' or \"\n",
        "        query += \"mimeType='application/vnd.openxmlformats-officedocument.wordprocessingml.document' or \"\n",
        "        query += \"mimeType='application/vnd.google-apps.document' or \"\n",
        "        query += \"mimeType='text/markdown') and trashed=false\"\n",
        "        \n",
        "        # Get list of files\n",
        "        results = service.files().list(\n",
        "            pageSize=max_results,\n",
        "            q=query,\n",
        "            fields=\"files(id, name, mimeType, createdTime, modifiedTime, size)\"\n",
        "        ).execute()\n",
        "        \n",
        "        files = results.get('files', [])\n",
        "        \n",
        "        if not files:\n",
        "            print(\"No supported files found.\")\n",
        "            return []\n",
        "        \n",
        "        print(f\"Found {len(files)} files. Processing...\")\n",
        "        \n",
        "        documents = []\n",
        "        for i, file in enumerate(files, 1):\n",
        "            if i % 5 == 0:\n",
        "                print(f\"Processed {i}/{len(files)} files...\")\n",
        "            \n",
        "            try:\n",
        "                # Download file content\n",
        "                if file['mimeType'] == 'application/vnd.google-apps.document':\n",
        "                    # Export Google Docs as plain text\n",
        "                    request = service.files().export_media(\n",
        "                        fileId=file['id'],\n",
        "                        mimeType='text/plain'\n",
        "                    )\n",
        "                else:\n",
        "                    # Download regular files\n",
        "                    request = service.files().get_media(fileId=file['id'])\n",
        "                \n",
        "                file_content = io.BytesIO()\n",
        "                downloader = MediaIoBaseDownload(file_content, request)\n",
        "                \n",
        "                done = False\n",
        "                while not done:\n",
        "                    status, done = downloader.next_chunk()\n",
        "                \n",
        "                # Extract text\n",
        "                content = extract_text_from_file(\n",
        "                    file_content.getvalue(),\n",
        "                    file['mimeType'],\n",
        "                    file['name']\n",
        "                )\n",
        "                \n",
        "                # Skip if no content or too short\n",
        "                if len(content.strip()) < 50:\n",
        "                    continue\n",
        "                \n",
        "                # Create document\n",
        "                doc = Document(\n",
        "                    page_content=content,\n",
        "                    metadata={\n",
        "                        'source': 'Google Drive',\n",
        "                        'type': 'file',\n",
        "                        'name': file['name'],\n",
        "                        'mime_type': file['mimeType'],\n",
        "                        'created': file.get('createdTime', ''),\n",
        "                        'modified': file.get('modifiedTime', ''),\n",
        "                        'file_id': file['id']\n",
        "                    }\n",
        "                )\n",
        "                documents.append(doc)\n",
        "                \n",
        "            except Exception as e:\n",
        "                print(f\"Skipped {file['name']}: {str(e)}\")\n",
        "                continue\n",
        "        \n",
        "        print(f\"Successfully processed {len(documents)} files\")\n",
        "        return documents\n",
        "        \n",
        "    except Exception as e:\n",
        "        print(f\"Error fetching Drive files: {str(e)}\")\n",
        "        return []\n",
        "\n",
        "# Test Drive fetching\n",
        "if creds:\n",
        "    drive_docs = fetch_drive_files(creds, max_results=30)\n",
        "    print(f\"\\nDrive Summary: {len(drive_docs)} files loaded\")\n",
        "    if drive_docs:\n",
        "        print(f\"Sample file: {drive_docs[0].metadata['name']}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Combine all documents\n",
        "all_documents = []\n",
        "\n",
        "if 'gmail_docs' in globals():\n",
        "    all_documents.extend(gmail_docs)\n",
        "if 'drive_docs' in globals():\n",
        "    all_documents.extend(drive_docs)\n",
        "\n",
        "print(f\"\\nTotal documents collected: {len(all_documents)}\")\n",
        "print(f\"- Gmail emails: {len([d for d in all_documents if d.metadata['source'] == 'Gmail'])}\")\n",
        "print(f\"- Drive files: {len([d for d in all_documents if d.metadata['source'] == 'Google Drive'])}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Split documents into chunks\n",
        "text_splitter = RecursiveCharacterTextSplitter(\n",
        "    chunk_size=1000,\n",
        "    chunk_overlap=200,\n",
        "    length_function=len,\n",
        ")\n",
        "\n",
        "chunks = text_splitter.split_documents(all_documents)\n",
        "print(f\"\\nSplit into {len(chunks)} chunks\")\n",
        "\n",
        "# Show chunk distribution\n",
        "gmail_chunks = len([c for c in chunks if c.metadata['source'] == 'Gmail'])\n",
        "drive_chunks = len([c for c in chunks if c.metadata['source'] == 'Google Drive'])\n",
        "print(f\"- Gmail chunks: {gmail_chunks}\")\n",
        "print(f\"- Drive chunks: {drive_chunks}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create embeddings and vector store\n",
        "print(\"\\nCreating embeddings and vector store...\")\n",
        "print(\"This may take a minute depending on the number of chunks...\")\n",
        "\n",
        "#embeddings = OpenAIEmbeddings()\n",
        "\n",
        "# Alternative: Use free HuggingFace embeddings\n",
        "from langchain.embeddings import HuggingFaceEmbeddings\n",
        "embeddings = HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-MiniLM-L6-v2\")\n",
        "\n",
        "# Delete existing vector store if present\n",
        "if os.path.exists(DB_NAME):\n",
        "    print(\"Deleting old vector store...\")\n",
        "    Chroma(persist_directory=DB_NAME, embedding_function=embeddings).delete_collection()\n",
        "\n",
        "# Create new vector store\n",
        "vectorstore = Chroma.from_documents(\n",
        "    documents=chunks,\n",
        "    embedding=embeddings,\n",
        "    persist_directory=DB_NAME\n",
        ")\n",
        "\n",
        "print(f\"Vector store created with {vectorstore._collection.count()} embeddings\")\n",
        "\n",
        "# Get embedding dimensions\n",
        "sample_embedding = vectorstore._collection.get(limit=1, include=[\"embeddings\"])[\"embeddings\"][0]\n",
        "print(f\"Embedding dimensions: {len(sample_embedding):,}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Part 6: 3D Visualization\n",
        "\n",
        "Visualize your knowledge base in 3D space. Blue points = Gmail, Green points = Drive files.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def visualize_vectorstore_3d(vectorstore):\n",
        "    \"\"\"Create 3D visualization of vector store\"\"\"\n",
        "    print(\"\\nCreating 3D visualization...\")\n",
        "    \n",
        "    # Get all embeddings and metadata\n",
        "    collection = vectorstore._collection\n",
        "    result = collection.get(include=['embeddings', 'documents', 'metadatas'])\n",
        "    \n",
        "    vectors = np.array(result['embeddings'])\n",
        "    documents = result['documents']\n",
        "    metadatas = result['metadatas']\n",
        "    \n",
        "    # Extract sources for coloring\n",
        "    sources = [meta['source'] for meta in metadatas]\n",
        "    colors = ['blue' if s == 'Gmail' else 'green' for s in sources]\n",
        "    \n",
        "    # Reduce to 3D using t-SNE\n",
        "    print(\"Reducing dimensions with t-SNE (this may take a moment)...\")\n",
        "    tsne = TSNE(n_components=3, random_state=42, perplexity=min(30, len(vectors)-1))\n",
        "    reduced_vectors = tsne.fit_transform(vectors)\n",
        "    \n",
        "    # Create hover text\n",
        "    hover_texts = []\n",
        "    for meta, doc in zip(metadatas, documents):\n",
        "        if meta['source'] == 'Gmail':\n",
        "            text = f\"Email<br>Subject: {meta.get('subject', 'N/A')[:50]}<br>From: {meta.get('sender', 'N/A')[:30]}\"\n",
        "        else:\n",
        "            text = f\"File<br>Name: {meta.get('name', 'N/A')[:50]}\"\n",
        "        text += f\"<br>Preview: {doc[:100]}...\"\n",
        "        hover_texts.append(text)\n",
        "    \n",
        "    # Create 3D scatter plot\n",
        "    fig = go.Figure(data=[go.Scatter3d(\n",
        "        x=reduced_vectors[:, 0],\n",
        "        y=reduced_vectors[:, 1],\n",
        "        z=reduced_vectors[:, 2],\n",
        "        mode='markers',\n",
        "        marker=dict(\n",
        "            size=5,\n",
        "            color=colors,\n",
        "            opacity=0.8,\n",
        "            line=dict(width=0.5, color='white')\n",
        "        ),\n",
        "        text=hover_texts,\n",
        "        hoverinfo='text'\n",
        "    )])\n",
        "    \n",
        "    fig.update_layout(\n",
        "        title='3D Knowledge Base Visualization (Blue=Gmail, Green=Drive)',\n",
        "        scene=dict(\n",
        "            xaxis_title='Dimension 1',\n",
        "            yaxis_title='Dimension 2',\n",
        "            zaxis_title='Dimension 3'\n",
        "        ),\n",
        "        width=900,\n",
        "        height=700,\n",
        "        margin=dict(r=20, b=10, l=10, t=40)\n",
        "    )\n",
        "    \n",
        "    print(\"Visualization ready!\")\n",
        "    return fig\n",
        "\n",
        "# Create visualization\n",
        "if len(chunks) > 0:\n",
        "    viz_fig = visualize_vectorstore_3d(vectorstore)\n",
        "    viz_fig.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": []
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create LLM and conversational chain\n",
        "llm = ChatOpenAI(temperature=0.7, model_name=MODEL)\n",
        "\n",
        "# Alternative: Ollama locally\n",
        "# llm = ChatOpenAI(temperature=0.7, model_name='llama3.2', base_url='http://localhost:11434/v1', api_key='ollama')\n",
        "\n",
        "# Set up conversation memory\n",
        "memory = ConversationBufferMemory(\n",
        "    memory_key='chat_history',\n",
        "    return_messages=True,\n",
        "    output_key='answer'\n",
        ")\n",
        "\n",
        "# Create retriever with more context\n",
        "retriever = vectorstore.as_retriever(\n",
        "    search_type=\"similarity\",\n",
        "    search_kwargs={\"k\": 10}  # Retrieve top 10 relevant chunks\n",
        ")\n",
        "\n",
        "# Create conversational chain\n",
        "conversation_chain = ConversationalRetrievalChain.from_llm(\n",
        "    llm=llm,\n",
        "    retriever=retriever,\n",
        "    memory=memory,\n",
        "    return_source_documents=True,\n",
        "    verbose=False\n",
        ")\n",
        "\n",
        "print(\"Conversational RAG chain ready!\")\n",
        "print(f\"Model: {MODEL}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Part 8: Test Queries\n",
        "\n",
        "Test the RAG system with some example queries.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Test the RAG system\n",
        "def test_query(question):\n",
        "    \"\"\"Test a query and show sources\"\"\"\n",
        "    print(f\"\\nQuestion: {question}\")\n",
        "    result = conversation_chain.invoke({\"question\": question})\n",
        "    print(f\"\\nAnswer: {result['answer']}\")\n",
        "    print(f\"\\nSources used: {len(result['source_documents'])} documents\")\n",
        "    for i, doc in enumerate(result['source_documents'][:3], 1):\n",
        "        source = doc.metadata['source']\n",
        "        if source == 'Gmail':\n",
        "            print(f\"{i}. Email: {doc.metadata.get('subject', 'N/A')[:50]}...\")\n",
        "        else:\n",
        "            print(f\"{i}. File: {doc.metadata.get('name', 'N/A')[:50]}\")\n",
        "    return result\n",
        "\n",
        "# Example queries\n",
        "print(\"Testing RAG system with sample queries...\")\n",
        "\n",
        "#test_query(\"What are the most recent emails about?\")\n",
        "# test_query(\"What files do I have related to Python or programming?\")\n",
        "#test_query(\"Summarize important information from my emails\")\n",
        "\n",
        "print(\"\\nReady for Gradio interface!\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Part 9: Gradio Interface\n",
        "\n",
        "Launch the interactive chat interface.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def chat_with_sources(question, history):\n",
        "    \"\"\"\n",
        "    Chat function that returns answer with sources\n",
        "    \"\"\"\n",
        "    try:\n",
        "        result = conversation_chain.invoke({\"question\": question})\n",
        "        answer = result['answer']\n",
        "        \n",
        "        # Add source information\n",
        "        sources = result['source_documents'][:3]\n",
        "        if sources:\n",
        "            answer += \"\\n\\n---\\n**Sources:**\\n\"\n",
        "            for i, doc in enumerate(sources, 1):\n",
        "                if doc.metadata['source'] == 'Gmail':\n",
        "                    answer += f\"{i}. Email: {doc.metadata.get('subject', 'N/A')[:60]}...\\n\"\n",
        "                else:\n",
        "                    answer += f\"{i}. File: {doc.metadata.get('name', 'N/A')[:60]}\\n\"\n",
        "        \n",
        "        return answer\n",
        "    except Exception as e:\n",
        "        return f\"Error: {str(e)}\"\n",
        "\n",
        "def get_stats():\n",
        "    \"\"\"Get knowledge base statistics\"\"\"\n",
        "    total_chunks = vectorstore._collection.count()\n",
        "    gmail_count = len([c for c in chunks if c.metadata['source'] == 'Gmail'])\n",
        "    drive_count = len([c for c in chunks if c.metadata['source'] == 'Google Drive'])\n",
        "    \n",
        "    stats = f\"\"\"\n",
        "**Knowledge Base Statistics**\n",
        "\n",
        "Total Chunks: {total_chunks}\n",
        "Gmail Emails: {gmail_count} chunks\n",
        "Drive Files: {drive_count} chunks\n",
        "\n",
        "Model: {MODEL}\n",
        "Embedding Dimensions: {len(sample_embedding):,}\n",
        "Retrieval: Top 10 relevant chunks per query\n",
        "    \"\"\"\n",
        "    return stats\n",
        "\n",
        "# Create Gradio interface\n",
        "with gr.Blocks(title=\"Personal Knowledge Worker\", theme=gr.themes.Soft()) as demo:\n",
        "    gr.Markdown(\"\"\"\n",
        "    # Personal Productivity Knowledge Worker\n",
        "    \n",
        "    Search across your Gmail emails and Google Drive files using AI.\n",
        "    \n",
        "    **Ask questions like:**\n",
        "    - \"What emails did I receive about [topic]?\"\n",
        "    - \"Find information about [subject] in my files\"\n",
        "    - \"Summarize recent communications about [project]\"\n",
        "    - \"What documents do I have related to [keyword]?\"\n",
        "    \"\"\")\n",
        "    \n",
        "    with gr.Tab(\"Chat\"):\n",
        "        chatbot = gr.ChatInterface(\n",
        "            chat_with_sources,\n",
        "            type=\"messages\",\n",
        "            examples=[\n",
        "                \"What are my most recent emails about?\",\n",
        "                \"What files do I have in my Drive?\",\n",
        "                \"Find information about projects or assignments\",\n",
        "                \"Summarize important communications\"\n",
        "            ],\n",
        "            title=\"Chat with Your Knowledge Base\"\n",
        "        )\n",
        "    \n",
        "    with gr.Tab(\"Statistics\"):\n",
        "        gr.Markdown(get_stats())\n",
        "        if 'viz_fig' in globals():\n",
        "            gr.Plot(viz_fig)\n",
        "    \n",
        "    with gr.Tab(\"Info\"):\n",
        "        gr.Markdown(\"\"\"\n",
        "        ## How It Works\n",
        "        \n",
        "        This system uses **Retrieval-Augmented Generation (RAG)** to:\n",
        "        1. Index your Gmail emails and Drive files\n",
        "        2. Convert them to vector embeddings\n",
        "        3. Search semantically (by meaning, not just keywords)\n",
        "        4. Generate contextual answers using GPT-4o-mini\n",
        "        \n",
        "        ## Privacy\n",
        "        - Your data is processed through OpenAI's API for embeddings and responses\n",
        "        - Vectors are stored locally in ChromaDB\n",
        "        - Original files remain in Gmail/Drive untouched\n",
        "        \n",
        "        ## Tips\n",
        "        - Be specific in your questions for better results\n",
        "        - The system searches both emails and files simultaneously\n",
        "        - Conversation history is maintained during your session\n",
        "        - Sources are shown below each answer\n",
        "        \n",
        "        ## Updating Your Knowledge Base\n",
        "        - Re-run the Gmail and Drive fetch cells to get new content\n",
        "        - Rebuild the vector store to include updated data\n",
        "        - The system will remember conversation context until you restart\n",
        "        \"\"\")\n",
        "\n",
        "print(\"\\nLaunching Gradio interface...\")\n",
        "demo.launch(inbrowser=True)\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
